#ifndef PROTOCOL_HH
#define PROTOCOL_HH

#include "socket.hh"
#include <iostream>
#include <string>
#include <stdlib.h>

using namespace std;
using namespace NM;

namespace DLIB_PROTO
{
enum PROTOCOL_CODES {
    SUCCESS = 0,
    DATA,
    PING,
    ERROR,
    CLOSE
};

enum PROTOCOL_DATA_EXCHANGE_CODES {
    EXCHANGE_MODULE,
    EXCHANGE_DATA,
    EXCHANGE_INFO,
    EXCHANGE_SERVER_INFO
};

enum PROTOCOL_DATA_EXCHANGE_INFO_CODES {
    TO_EXECUTE,
    EXECUTION_RESULT,
    INVALID
};

#define PROTOCOL_NAME_SIZE                  100
#define PROTOCOL_INFO_STRUCT_SIZE           (PROTOCOL_NAME_SIZE + (2 * sizeof(PROTOCOL_DATA_EXCHANGE_INFO_CODES)))
#define PROTOCOL_SERVER_INFO_STRUCT_SIZE    (PROTOCOL_NAME_SIZE + sizeof(int))
#define PROTOCOL_DATA_PAQUET_SIZE           (PROTOCOL_INFO_STRUCT_SIZE + 916)
#define PROTOCOL_INFO_PADDING_SIZE          (PROTOCOL_DATA_PAQUET_SIZE - PROTOCOL_INFO_STRUCT_SIZE)
#define PROTOCOL_SERVER_INFO_PADDING_SIZE   (PROTOCOL_DATA_PAQUET_SIZE - PROTOCOL_SERVER_INFO_STRUCT_SIZE)

struct __attribute__((packed))          protocol_data_exchange_s
{
    PROTOCOL_DATA_EXCHANGE_CODES        _paquetType;
    int                                 _paquetSize;
    char                                _paquetData[PROTOCOL_DATA_PAQUET_SIZE];
};

struct __attribute__((packed))          protocol_data_exchange_info_s
{
    PROTOCOL_DATA_EXCHANGE_INFO_CODES   _dataExchanceType;
    PROTOCOL_CODES                      _executionStatus;
    int                                 _moduleSize;
    int                                 _dataSize;
    char                                _moduleName[PROTOCOL_NAME_SIZE];
    char                                _padding[PROTOCOL_INFO_PADDING_SIZE];
};

struct __attribute__((packed))          protocol_data_exchange_server_info_s
{
    int                                 _serverPort;
    char                                _serverAddress[PROTOCOL_NAME_SIZE];
    char                                _padding[PROTOCOL_SERVER_INFO_PADDING_SIZE];
};

typedef protocol_data_exchange_s                ProtocolDataStruct;
typedef protocol_data_exchange_info_s           ProtocolDataInfoStruct;
typedef protocol_data_exchange_server_info_s    ProtocolDataServerInfoStruct;

class Protocol
{
private: // variables
    enum PROTOCOL_MODE {
        CLIENT,
        DISPATCHER,
        SERVER,
        NONE
    };

    PROTOCOL_MODE       _mode;
    bool                _initialized;
    bool                _connectionConfirmed;
    Socket              *_network;
    void                *_moduleCode;
    int                 _moduleCodeSize;
    void                *_moduleData;
    int                 _moduleDataSize;
    string              _moduleName;
    bool                _moduleCodeToFree;
    bool                _moduleDataToFree;

public: // interface
    Protocol();
    ~Protocol();

    void InitServer(Socket *network);
    void InitClient(Socket *network);
    void InitDispatcher(Socket *network);

    bool RecvConnectionConfirmation();
    int RecvCode();
    bool sendCode(int code);
    bool RecvData();
    bool SendConnectionConfirmation();
    bool SendData(bool executionSuccess = true);
    bool SendServerInfo(string address, int port);
    bool RecvServerInfo(string *address, int *port);

    void GetModuleCode(void **moduleCode, int *moduleCodeSize);
    void GetModuleData(void **moduleData, int *moduleDataSize);
    void SetModuleCode(void *moduleCode, int moduleCodeSize);
    void SetModuleData(void *moduleData, int moduleDataSize);
    string GetModuleName();
    void SetModuleName(string name);

    int GetId();

private: // internals
    bool receiveModuleData();
    bool sendModuleData(bool success);

};

}

#endif /* PROTOCOL_HH */
